/**
 * 
 */
package org.yelongframework.json.gson;

import java.util.LinkedList;
import java.util.List;

import org.yelongframework.core.util.map.LinkedMultiValueMap;
import org.yelongframework.core.util.map.MultiValueMap;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

/**
 * 类型排除策略
 * 
 * @since 1.0.0
 */
public class ClassExclusionStrategy implements ExclusionStrategy {

	/** 忽略的类型 */
	private final List<Class<?>> ignoreClasss = new LinkedList<>();

	/** 忽略类型的字段（只忽略该类型中指定的字段），如果这个类已经被忽略({@link #ignoreClasss}，那么将不起作用 */
	private final MultiValueMap<Class<?>, String> ignoreClassFields = new LinkedMultiValueMap<>();

	/** 只操作的类型 */
	private final List<Class<?>> onlyOperationClasss = new LinkedList<>();

	/** 只操作的类型字段，如果这个类已经被忽略({@link #onlyOperationClasss}，那么将不起作用 */
	private final MultiValueMap<Class<?>, String> onlyOperationClassFields = new LinkedMultiValueMap<>();

	// ==================================================忽略类==================================================

	/**
	 * 添加忽略的类
	 * 
	 * @param classes 忽略的类集合
	 */
	public void addIgnoreClasss(Class<?>... classes) {
		if (null != classes) {
			for (Class<?> cls : classes) {
				this.ignoreClasss.add(cls);
			}
		}
	}

	/**
	 * 添加忽略的类
	 * 
	 * @param classes 忽略的类数组
	 */
	public void addIgnoreClasss(List<Class<?>> classes) {
		this.ignoreClasss.addAll(classes);
	}

	/**
	 * @return 忽略的类集合
	 */
	public List<Class<?>> getIgnoreClasss() {
		return ignoreClasss;
	}

	// ==================================================忽略字段==================================================

	/**
	 * 添加忽略的类的属性
	 * 
	 * @param classes    忽略的类
	 * @param fieldNames 忽略类中的属性
	 */
	public void addIgnoreClassFields(Class<?> c, String... fieldNames) {
		if (null != fieldNames) {
			for (String fieldName : fieldNames) {
				this.ignoreClassFields.add(c, fieldName);
			}
		}
	}

	/**
	 * 添加忽略的类的属性
	 * 
	 * @param classes    忽略的类
	 * @param fieldNames 忽略类中的属性
	 */
	public void addIgnoreClassFields(Class<?> c, List<String> fieldNames) {
		this.ignoreClassFields.addAll(c, fieldNames);
	}

	/**
	 * 设置忽略的类的属性
	 * 
	 * @param classes    忽略的类
	 * @param fieldNames 忽略类中的属性
	 */
	public void setIgnoreClassFields(Class<?> c, String... fieldNames) {
		if (null != fieldNames) {
			for (String fieldName : fieldNames) {
				this.ignoreClassFields.set(c, fieldName);
			}
		}
	}

	/**
	 * 设置忽略的类的属性
	 * 
	 * @param classes    忽略的类
	 * @param fieldNames 忽略类中的属性
	 */
	public void setIgnoreClassFields(Class<?> c, List<String> fieldNames) {
		this.ignoreClassFields.put(c, fieldNames);
	}

	/**
	 * @return 忽略的类的属性
	 */
	public MultiValueMap<Class<?>, String> getIgnoreClassFields() {
		return ignoreClassFields;
	}

	// ==================================================只操作类==================================================

	/**
	 * 设置只操作的类型 设置之后忽略的类将不起作用，已只操作的类型为准
	 * 
	 * @param classes 只操作的类
	 */
	public void addOnlyOperationClasss(Class<?>... classes) {
		if (null != classes) {
			for (Class<?> cls : classes) {
				onlyOperationClasss.add(cls);
			}
		}
	}

	/**
	 * 设置只操作的类型 设置之后忽略的类将不起作用，已只操作的类型为准
	 * 
	 * @param classes 只操作的类
	 */
	public void addOnlyOperationClasss(List<Class<?>> classes) {
		onlyOperationClasss.addAll(classes);
	}

	/**
	 * @return 只操作的类型
	 */
	public List<Class<?>> getOnlyOperationClasss() {
		return onlyOperationClasss;
	}

	// ==================================================只操作类字段==================================================

	/**
	 * 添加只操作的类型属性。 如果这个类型不存在只操作的类型中将不起作用。
	 * 
	 * @param classes    只操作的类
	 * @param fieldNames 只操作类的属性
	 * @return this
	 */
	public void addOnlyOperationClassFields(Class<?> c, String... fieldNames) {
		if (null != fieldNames) {
			for (String fieldName : fieldNames) {
				this.onlyOperationClassFields.add(c, fieldName);
			}
		}
	}

	/**
	 * 添加只操作的类型属性。 如果这个类型不存在只操作的类型中将不起作用。
	 * 
	 * @param classes    只操作的类
	 * @param fieldNames 只操作类的属性
	 * @return this
	 */
	public void addOnlyOperationClassFields(Class<?> c, List<String> fieldNames) {
		this.onlyOperationClassFields.addAll(c, fieldNames);
	}

	/**
	 * 设置只操作的类型属性。 如果这个类型不存在只操作的类型中将不起作用。
	 * 
	 * @param classes    只操作的类
	 * @param fieldNames 只操作类的属性
	 * @return this
	 */
	public void setOnlyOperationClassFields(Class<?> c, String... fieldNames) {
		if (null != fieldNames) {
			for (String fieldName : fieldNames) {
				this.onlyOperationClassFields.set(c, fieldName);
			}
		}
	}

	/**
	 * 设置只操作的类型属性。 如果这个类型不存在只操作的类型中将不起作用。
	 * 
	 * @param classes    只操作的类
	 * @param fieldNames 只操作类的属性
	 * @return this
	 */
	public void setOnlyOperationClassFields(Class<?> c, List<String> fieldNames) {
		this.onlyOperationClassFields.put(c, fieldNames);
	}

	/**
	 * @return 只操作的类型属性
	 */
	public MultiValueMap<Class<?>, String> getOnlyOperationClassFields() {
		return onlyOperationClassFields;
	}

	// ==================================================跳过字段==================================================

	@Override
	public boolean shouldSkipField(FieldAttributes f) {
		if (this.onlyOperationClassFields.containsKey(f.getDeclaringClass())) {
			List<String> fieldList = this.onlyOperationClassFields.get(f.getDeclaringClass());
			if (null == fieldList) {
				return false;
			}
			return !fieldList.contains(f.getName());
		} else {
			List<String> fieldList = ignoreClassFields.get(f.getDeclaringClass());
			if (null == fieldList) {
				return false;
			}
			return fieldList.contains(f.getName());
		}
	}

	// ==================================================跳过类==================================================

	@Override
	public boolean shouldSkipClass(Class<?> clazz) {
		if (onlyOperationClasss.isEmpty()) {
			return this.ignoreClasss.contains(clazz);
		} else {
			return !this.onlyOperationClasss.contains(clazz);
		}
	}

}
